package adfc2.render3d.mesh;

import adfc2.render3d.IRenderable;
import adfc2.render3d.TextureReference;

import javax.media.opengl.GL;

/**
 * Примитив --- цилиндр
 */
public class Cylinder extends Mesh {
    /**
     * @param bottomcenter Координаты центра низа
     * @param rrh          Радиусы по x,y; высота
     * @param texture      Текстура
     */
    public Cylinder(double[] color, double[] bottomcenter, double[] rrh, TextureReference texture) {
        super(primitivesFor(color, bottomcenter, rrh, texture));
    }


    public static Cylinder round(double[] color, double[] bottomcenter, double r, double h, TextureReference texture) {
        return new Cylinder(color, bottomcenter, new double[]{r, r, h}, texture);
    }

    private static IRenderable[] primitivesFor(double[] color, double[] bottomcenter, double[] rrh, TextureReference texture) {
        IRenderable[] primitives = new IRenderable[3];

        double cx = bottomcenter[0];
        double cy = bottomcenter[1];
        double cz = bottomcenter[2];
        double rx = rrh[0];
        double ry = rrh[1];
        double h = rrh[2];

        // крышка и дно
        double[] top_vertices = new double[(Ellipsoid.N_SLICES + 2) * 3];
        double[] top_texcoord = (texture == null) ? null : new double[(Ellipsoid.N_SLICES + 2) * 2];
        double[] top_normals = new double[(Ellipsoid.N_SLICES + 2) * 3];
        double[] bottom_vertices = new double[top_vertices.length];
        double[] bottom_texcoord = (texture == null) ? null : new double[top_texcoord.length];
        double[] bottom_normals = new double[top_normals.length];
        // центры шапок
        top_vertices[0] = cx;
        top_vertices[1] = cy;
        top_vertices[2] = cz + h;
        bottom_vertices[0] = cx;
        bottom_vertices[1] = cy;
        bottom_vertices[2] = cz;
        if (texture != null) {
            top_texcoord[0] = 0.5;
            top_texcoord[1] = 0.5;
            bottom_texcoord[0] = 0.5;
            bottom_texcoord[1] = 0.5;
        }
        top_normals[0] = 0;
        top_normals[1] = 0;
        top_normals[2] = 1;
        bottom_normals[0] = 0;
        bottom_normals[1] = 0;
        bottom_normals[2] = -1;
        for (int i = 0; i <= Ellipsoid.N_SLICES; i++) {
            int ii = i * 3 + 3;
            top_vertices[ii + 0] = cx + Ellipsoid.COSXY_VALUES[i] * rx;
            top_vertices[ii + 1] = cy + Ellipsoid.SINXY_VALUES[i] * ry;
            top_vertices[ii + 2] = cz + h;
            top_normals[ii + 0] = 0;
            top_normals[ii + 1] = 0;
            top_normals[ii + 2] = 1;
            if (texture != null) {
                top_texcoord[i * 2 + 2] = (1 - Ellipsoid.COSXY_VALUES[i]) / 2;
                top_texcoord[i * 2 + 3] = (1 - Ellipsoid.SINXY_VALUES[i]) / 2;
                bottom_texcoord[i * 2 + 2] = top_texcoord[i * 2 + 2];
                bottom_texcoord[i * 2 + 3] = top_texcoord[i * 2 + 3];
            }
        }
        // окружность нижней шапки - проход в обратном порядке (culling)
        for (int i = 0; i <= Ellipsoid.N_SLICES; i++) {
            int ii = i * 3 + 3;
            int bi = (Ellipsoid.N_SLICES + 1 - i) * 3;
            bottom_vertices[ii + 0] = top_vertices[bi + 0];
            bottom_vertices[ii + 1] = top_vertices[bi + 1];
            bottom_vertices[ii + 2] = cz;
            bottom_normals[ii + 0] = 0;
            bottom_normals[ii + 1] = 0;
            bottom_normals[ii + 2] = -1;
        }

        // бока
        int body_npoints = (Ellipsoid.N_SLICES + 1) * 2;
        double[] body_vertices = new double[body_npoints * 3];
        double[] body_texcoord = (texture == null) ? null : new double[body_npoints * 2];
        double[] body_normals = new double[body_npoints * 3];

        for (int j = 0; j <= Ellipsoid.N_SLICES; j++) {
            int vii = j * 3 * 2;
            // верхняя вершина, нижняя вершина
            body_vertices[vii + 0] = cx + Ellipsoid.COSXY_VALUES[j] * rx;
            body_vertices[vii + 1] = cy + Ellipsoid.SINXY_VALUES[j] * ry;
            body_vertices[vii + 2] = cz + h;
            body_vertices[vii + 3] = cx + Ellipsoid.COSXY_VALUES[j] * rx;
            body_vertices[vii + 4] = cy + Ellipsoid.SINXY_VALUES[j] * ry;
            body_vertices[vii + 5] = cz;
            body_normals[vii + 0] = Ellipsoid.COSXY_VALUES[j];
            body_normals[vii + 1] = Ellipsoid.SINXY_VALUES[j];
            body_normals[vii + 2] = 0;
            body_normals[vii + 3] = Ellipsoid.COSXY_VALUES[j];
            body_normals[vii + 4] = Ellipsoid.SINXY_VALUES[j];
            body_normals[vii + 5] = 0;
            if (texture != null) {
                int tii = j * 2 * 2;
                body_texcoord[tii + 0] = 1.0 * j / Ellipsoid.N_SLICES;
                body_texcoord[tii + 1] = 0;
                body_texcoord[tii + 2] = 1.0 * j / Ellipsoid.N_SLICES;
                body_texcoord[tii + 3] = 1;
            }
        }

        primitives[0] = new Primitive(GL.GL_TRIANGLE_FAN, color, top_vertices, top_texcoord, top_normals, texture);
        primitives[1] = new Primitive(GL.GL_TRIANGLE_FAN, color, bottom_vertices, bottom_texcoord, bottom_normals, texture);
        primitives[2] = new Primitive(GL.GL_QUAD_STRIP, color, body_vertices, body_texcoord, body_normals, texture);
        return primitives;
    }

}
